#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# g-DrcomClient-cli , a Novel Command-Line Tool for Drcom-Client
#
#       g-drcomclient-cli.py
#
# Copyright (c) 2008, Henry Huang <henry.s.huang@gmail.com>

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

import fcntl,struct

import time,os
from os import listdir,getcwd,path
import socket,md5
from operator import xor

global conf_name
conf_name='gdrcom.conf'

global conf_path
conf_path='/home/'+os.environ['USER']+'/.gdrcom'

class drcom_client:

	def get_ifname(self):

		ifname_space=os.popen("/sbin/ifconfig -s| awk '{print $1}'").read()
		ifname_start=ifname_space.find('\n')
		ifname_name=[]
		while(ifname_start!=-1):
			ifname_end=ifname_space.find('\n',ifname_start+1)
			if ifname_end==-1:
				break
			ifname=ifname_space[ifname_start+1:ifname_end]
			ifname_name.append(ifname)
			ifname_start=ifname_end
		ifname_status=os.popen("/sbin/ifconfig | awk '{print $1,$3}'").read()
		for i in range(0,len(ifname_name)-1):
			ifname_start=ifname_status.find(ifname_name[i])
			ifname_end=ifname_status.find(ifname_name[i+1])
			ifname_region=ifname_status[ifname_start:ifname_end]
			ifname_index=ifname_region.find('RUNNING')
			if ifname_index!=-1 and 'lo' not in ifname_name[i]:
				return ifname_name[i]
				
		tag='Error 01'	
		self.show_info(tag,'No active network card!')
		self.quit()

	def get_ip_addr(self):
		try:
			s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
			ip_addr=fcntl.ioctl(s.fileno(),0x8915,\
				struct.pack('256s', self.ifname[:15]))[20:24]
		except:
			tag='Error 02'			
			self.show_info(tag,'Can not get your IP address!')
			self.quit()

		return ip_addr


	def get_mac_addr(self):
		try:
			s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
			mac_addr=fcntl.ioctl(s.fileno(), 0x8927,\
				struct.pack('256s', self.ifname[:15]))[18:24]
		except:
			tag='Error 03'
			self.show_info(tag,'Can not get your MAC address!')
			self.quit()
		
		return mac_addr

	def read_conf(self):
		pathname=conf_path
		if path.exists(pathname)==False:
			os.mkdir(pathname)
		file_path=path.join(pathname,conf_name)
		list_dir=listdir(pathname)
		if conf_name in list_dir:
			f=file(file_path,'r')
			account_pass=f.read()		
			f.close()
			account_end=account_pass.find(',')
			if account_end==-1:
				self.passwd_flag=False		
				self.account=''
				self.password=''
				return False
			self.account=account_pass[:account_end]
			if account_pass[len(account_pass)-1].isdigit()==True:
				self.password=account_pass[account_end+1:len(account_pass)]
			else:
				self.password=account_pass[account_end+1:len(account_pass)-1]
			self.passwd_flag=True
			return True

		self.passwd_flag=False		
		self.account=''
		self.password=''
		return False		

	def init_conf(self):
		self.BUFFER=512
		self.server_brand='Drco'
		self.server_ip='192.168.168.168'
		self.server_port=61440
		self.ifname=self.get_ifname()
		self.md5_tail='\x14\x00\x07\x0b'
		self.host_ip=self.get_ip_addr()
		self.host_ip_dec=socket.inet_ntoa(self.host_ip)
		self.host_name='\x00'*16
		self.kernel_version='\x00'*12
		self.mac_addr=self.get_mac_addr()
		self.packet_id={
			'client_start_request':'\x01\x00\x00\x04',
			'server_start_response':'\x02\x00\x00\x04',
			'client_login':'\x03\x01',
			'server_login':'\x04\x00\x00\x05\x03',
			'client_keeplive_1':'\xfe',
			'client_keeplive_2':'\xff',
			'server_keeplive':'\x4d\x26',
			'client_end_request':'\x01\x00\x06\x04',
			'server_end_response':'\x02\x00\x06\x04',
			'client_logout':'\x06\x01',
			'server_logout':'\x04\x00\x00\x05\x03',
			}
		self.keep_live_count=0

	def hex_xor(self,xor_oper1,xor_oper2,oper_len):
		xor_result=''
		for i in range(0,oper_len):
			temp=chr(xor(ord(xor_oper1[i]),ord(xor_oper2[i])))
			xor_result=xor_result+temp
		return xor_result

	def show_hex(self,hex_oper):
		for i in range(0,len(hex_oper)):
			print hex(ord(hex_oper[i])),
		print '\n'

	def show_dec(self,dec_oper):
		dec_result=''
		for i in range(0,len(dec_oper)):
			dec_hex=hex(ord(dec_oper[i]))[2:]
			dec_result=dec_result+'0'*(2-len(dec_hex))+dec_hex
		return str(int(dec_result,16))

	def md5_key(self,md5_content):
		md5_temp=md5.new()
		md5_temp.update(md5_content)
		return md5_temp.digest()

	def drcom_login(self):

		try:
			if self.login_flag==[True,False]:
				tag='Error 04'
				self.show_info(tag,'You have already LOGIN!')
				#return False
				self.quit()
		except:
			self.login_flag=[False,False]						

		self.init_conf()	
		proc_name='client_start_request'
		self.drcom_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
		try:	
			self.drcom_sock.bind((self.host_ip_dec,self.server_port))
			self.drcom_sock.settimeout(1.0)
		except:
			tag='Error 05'
			self.show_info(tag,'Fail to bind your address!')
			#return False
			self.quit()

		send_data=self.packet_id[proc_name]+'\x01'+'\x00'*15
		try:
			self.drcom_sock.sendto(send_data,(self.server_ip,self.server_port))		
			recv_data=self.drcom_sock.recvfrom(self.BUFFER)
			
		except:
			tag='Error 06'
			self.show_info(tag,'Connection lost when login [request] !')
			#return False
			self.quit()		
			
		self.service_identifier=recv_data[0][4:8]
		proc_name='client_login'
		data_head=self.packet_id[proc_name]+'\x06\x1f'
		md5_content=self.packet_id[proc_name]+self.service_identifier+self.password
		self.first_md5=self.md5_key(md5_content)
		NULL_1='\x00'*25+'\x18\x01'
		mac_length=len(self.mac_addr)
		mac_xor=self.hex_xor(self.mac_addr,self.first_md5,mac_length)
		md5_content='\x01'+self.password+self.service_identifier+'\x00'*4
		second_md5=self.md5_key(md5_content)
		NULL_2='\x00'*12
		data_front=data_head+self.first_md5+self.account+NULL_1+\
			mac_xor+second_md5+'\x02'+\
			self.host_ip+NULL_2
		third_md5=self.md5_key(data_front)[0:8]
		NULL_3='\x00'*34+'\x02'+'\x00'*3+'\x04'+'\x00'*7+'\x02'+'\x00'*3
		NULL_4='\x00'*(16*7+4)+'\x01\x00\x01\x08'+'\x00'*8
		send_data=data_front+third_md5+'\x00'*5+\
			self.host_name+NULL_3+self.kernel_version+NULL_4
		try:
			self.drcom_sock.sendto(send_data,(self.server_ip,self.server_port))
			recv_data=self.drcom_sock.recvfrom(self.BUFFER)
		except:
			tag='Error 06'
			self.show_info(tag,'Connection lost when login [response] !')
			#return False
			self.quit()

		if len(recv_data[0])==5:
			tag='Login Failed!'
			self.show_info(tag,'Wrong account name or password!')
			self.login_flag=[False,False]
			#return False
			self.quit()
		
		self.login_flag=[True,False]

		cost_time=recv_data[0][6]+recv_data[0][5]
		cost_traffic=recv_data[0][12]+recv_data[0][11]+recv_data[0][10]+recv_data[0][9]
		cost_money=recv_data[0][14]+recv_data[0][13]

		self.service_tail=recv_data[0][23:39]
		self.show_cost(cost_time,cost_traffic,cost_money)

		recv_data=self.drcom_sock.recvfrom(self.BUFFER)

	def drcom_logout(self):
		try:
			if self.login_flag==[False,True]:
				tag='Error 07'
				self.show_info(tag,'You have already LOGOUT!')
				#return False
				self.quit()
			if self.login_flag==[False,False]:
				tag='Error 08'
				self.show_info(tag,'You should LOGIN first!')
				#return False
				self.quit()
		except:
			tag='Error 08'
			self.show_info(tag,'You should LOGIN first!')
			#return False
			self.quit()

		proc_name='client_end_request'
		send_data=self.packet_id[proc_name]+'\x01'+'\x00'*15
		try:
			self.drcom_sock.sendto(send_data,(self.server_ip,self.server_port))
			recv_data=self.drcom_sock.recvfrom(self.BUFFER)
		except:
			tag='Error 09'
			self.show_info(tag,'Connection lost when logout [request] !')
			#return False
			self.quit()
			

		if recv_data[0][0]=='\x4d':
			recv_data=self.drcom_sock.recvfrom(self.BUFFER)

		self.service_identifier=recv_data[0][4:8]
		proc_name='client_logout'
		data_head=self.packet_id[proc_name]+'\x06\x1f'
		md5_content=self.packet_id[proc_name]+self.service_identifier+self.password
		fifth_md5=self.md5_key(md5_content)
		NULL_1='\x00'*27
		mac_xor=self.hex_xor(self.mac_addr,fifth_md5,len(self.mac_addr))
		send_data=self.packet_id[proc_name]+'\x00\x1f'+fifth_md5+self.account+\
			NULL_1+mac_xor+self.service_tail

		try:
			self.drcom_sock.sendto(send_data,(self.server_ip,self.server_port))
			recv_data=self.drcom_sock.recvfrom(self.BUFFER)
		except:
			tag='Error 09'
			self.show_info(tag,'Connection lost when logout [response] !')
			#return False
			self.quit()

		if recv_data[0][0]=='\x4d':
			recv_data=self.drcom_sock.recvfrom(self.BUFFER)

		if recv_data[0][:4]!='\x04\x00\x00\x05':
			tag='Logout failed!'
			self.show_info(tag,'Please check!')
			#return False
			self.quit()

		cost_time=recv_data[0][6]+recv_data[0][5]
		cost_traffic=recv_data[0][12]+recv_data[0][11]+recv_data[0][10]+recv_data[0][9]
		cost_money=recv_data[0][14]+recv_data[0][13]
		tag='Logout successful!'
		self.show_cost(cost_time,cost_traffic,cost_money)
		try:
			self.drcom_sock.close()
		except:
			tag='Error 10'
			self.show_info(tag,'Fail to Close Socket!')
			self.quit()
			#return False

		self.login_flag=[False,True]

	def keep_live(self):
		if self.login_flag==[True,False]:
			
			if self.keep_live_count>6:
				tag='Error 11'
				self.show_info(tag,'Connection lost when keep_live [response] !')
				self.login_flag=[False,False]
				self.keep_live_count=0
				#return False	
				self.quit()

			recv_data=self.keep_live_2()
			if len(recv_data)==4:				
				self.keeplive_tag=recv_data[2:]
				self.keep_live_1()
				self.keep_live_count=0
			return True
		else:
			return False
	
	def keep_live_2(self):
		proc_name='client_keeplive_2'
		send_data=self.packet_id[proc_name]+self.first_md5+'\x00'*3+self.service_tail
		try:
			self.drcom_sock.sendto(send_data,(self.server_ip,self.server_port))
		except:
			tag='Error 11'
			self.show_info(tag,'Connection lost when keep_live [request] !')
			self.quit()
		try:		
			recv_data=self.drcom_sock.recvfrom(self.BUFFER)
		except:
			recv_data=['']
			self.keep_live_count=self.keep_live_count+1			
		
		return recv_data[0]
	
	def keep_live_1(self):
		proc_name='client_keeplive_1'
		md5_content=self.packet_id[proc_name]+self.keeplive_tag[0]+'\x01'+\
			self.md5_tail+self.keeplive_tag
		fourth_md5=self.md5_key(md5_content)
		send_data=self.packet_id[proc_name]+self.keeplive_tag[0]+'\x01'\
			+fourth_md5+'\xff'+self.service_tail
		try:
			self.drcom_sock.sendto(send_data,(self.server_ip,self.server_port))
		except:
			tag='Error 11'
			self.show_info(tag,'Connection lost when keep_live [request] !')
			self.quit()

	def password_save(self):
		if self.pwd_save_flag=='Y' or pwd_save_flag=='':
			if len(self.account)==0 and len(self.password)==0:
				f=file(path.join(conf_path,conf_name),'w')
				f.write('')
				f.close()
			else:
				f=file(path.join(conf_path,conf_name),'w')
				f.write(self.account+','+self.password)
				f.close()
		else:
			f=file(path.join(conf_path,conf_name),'w')
			f.write('')
			f.close()

	
	def __init__(self):
		
		self.read_conf()
		self.login_flag=[False,False]
		try:
			self.drcom_login()
        		while True: 
				if self.login_flag==[True,False]:
					self.keep_live()
					time.sleep(20)			
				continue
		except KeyboardInterrupt:
				self.drcom_logout()
		
	def show_cost(self,cost_time,cost_traffic,cost_money):

		info='Used '+self.show_dec(cost_time)+' Min, '+self.show_dec(cost_traffic)+\
			' KB'+'\n'+'Balance '
		
		if len(str(self.show_dec(cost_money)))==4:
			info=info+self.show_dec(cost_money)[0:2]+'.'+self.show_dec(cost_money)[2:4]+' yuan.'
		if len(str(self.show_dec(cost_money)))==3:
			info=info+self.show_dec(cost_money)[0:1]+'.'+self.show_dec(cost_money)[1:3]+' yuan.'
		if len(str(self.show_dec(cost_money)))==2:
			info=info+'0'+'.'+self.show_dec(cost_money)[0:2]+' yuan.'
		if len(str(self.show_dec(cost_money)))==1:
			info=info+'0'+'.'+'0'+self.show_dec(cost_money)+' yuan.'
		
		tag='Login successful!'
		self.show_info(tag,info)		
		
    	def quit(self): raise SystemExit

	def show_info(self,tag,info):
		print '-'*30
		print tag
		print info
		print '-'*30



def main():
   	app = drcom_client()

if __name__ == '__main__': main()



